package cn.bingoogolapple.bgabanner;

import cn.bingoogolapple.bgabanner.transformer.PageTransformer;
import cn.bingoogolapple.bgabanner.transformer.TransitionEffect;
import cn.bingoogolapple.bgabanner.utils.DensityUtils;
import cn.bingoogolapple.bgabanner.utils.TypedAttrUtils;
import ohos.aafwk.ability.Ability;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Color;
import ohos.agp.utils.TextAlignment;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.global.resource.NotExistException;
import ohos.global.resource.WrongTypeException;
import ohos.multimodalinput.event.TouchEvent;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.*;

import static ohos.agp.components.DependentLayout.LayoutConfig.ALIGN_PARENT_LEFT;
import static ohos.agp.components.Text.TruncationMode.ELLIPSIS_AT_END;
import static ohos.agp.utils.LayoutAlignment.CENTER;

public class BGABanner extends DependentLayout implements PageSliders.PageChangedListener, Component.TouchEventListener, Component.EstimateSizeListener {
    private static final Image.ScaleMode[] sScaleTypeArray = {
            Image.ScaleMode.ZOOM_CENTER,
            Image.ScaleMode.ZOOM_START,
            Image.ScaleMode.ZOOM_END,
            Image.ScaleMode.STRETCH,
            Image.ScaleMode.CENTER,
            Image.ScaleMode.INSIDE,
            Image.ScaleMode.CLIP_CENTER
    };
    private static final int RMP = DependentLayout.LayoutConfig.MATCH_PARENT;
    private static final int RWC = DependentLayout.LayoutConfig.MATCH_CONTENT;
    private static final int NO_PLACEHOLDER_DRAWABLE = -1;
    private static final String IN_ALIGN_PARENT_LEFT = "LEFT";
    private static final String IN_ALIGN_PARENT_RIGHT = "RIGHT";
    private static final String TEXT_CENTER = "CENTER";
    private static final String OUT_ALIGN_PARENT_TOP = "TOP";
    private static final String OUT_ALIGN_PARENT_BOTTOM = "BOTTOM";
    private Map<Integer, Component> mViewCache = new HashMap<>();
    private BGAViewPager mViewPager;
    private List<Component> mHackyViews;
    private List<Component> mViews;
    private List<String> mTips;
    private DirectionalLayout mPointRealContainerLl;
    private Text indicatorText;
    private boolean isAuto = true;
    private int autoTime = 3000;
    private int switchPageTime = 800;
    private int indicator_TopOrBottom = LayoutConfig.ALIGN_PARENT_BOTTOM;
    private String indicator_TopOrBottomStr = "BOTTOM";
    private int indicatorLeftRightMargin;
    private int indicatorTopBottomMargin;
    private int indicatorContainerLeftRightPadding;
    private int textSize;
    private Color textColor = Color.WHITE;
    private AutoPlayTask mAutoPlayTask;
    private EventHandler handler;
    private float mPageScrollPositionOffset;
    private Image mPlaceholderIv;
    private Image.ScaleMode mScaleType = Image.ScaleMode.CLIP_CENTER;
    private Context context;
    private PageTransformer pageTransformer;
    private int placeholderDrawableResId = NO_PLACEHOLDER_DRAWABLE;
    private List<? extends Object> mModels;
    private Delegate mDelegate;
    private Adapter mAdapter;
    private PageSliders.PageChangedListener mOnPageChangeListener;
    private DependentLayout zsqPostionLayout;
    private boolean isNumType = false;
    private Text mNumberIndicatorTv;
    private Color numberTextColor = Color.WHITE;
    private int numberTextSize;
    private Color numberindicatorbackground = Color.TRANSPARENT;
    private Color indicatorBgColor = Color.TRANSPARENT;
    private boolean isOnlyOnePageShow = false;
    private int mContentBottomMargin;
    private float heightRatio = 0;
    private boolean mAllowUserScrollable = true;
    private Component mSkipView;
    private Component mEnterView;
    private GuideDelegate mGuideDelegate;
    private boolean mIsFirstInvisible = true;
    private int iconYesElement = -1;
    private int iconNoElement = -1;
    private int numIndicatorId = 0x6724;
    private int mPointRealContainerLlId = 0x6723;

    // 指示器的位置 可选值：左(ALIGN_PARENT_LEFT) 中(HORIZONTAL_CENTER) 右(ALIGN_PARENT_RIGHT)  默认是 HORIZONTAL_CENTER
    private int dicatorLeftOrRightOrCenter = LayoutConfig.HORIZONTAL_CENTER;

    // 用于转换
    private String dicatorLeftOrRightOrCenterStr = "CENTER";
    private String dicatorTextLeftOrRightOrCenter = "LEFT";
    private int dicatorTextGravity = LayoutConfig.ALIGN_PARENT_LEFT;

    /**
     * constructor
     *
     * @param context
     */
    public BGABanner(Context context) {
        super(context);
        this.context = context;
        setEstimateSizeListener(this);
        setTouchEventListener(this);
        initDefaultAttrs(context);
        initView(context);
        setTouchEventListener(this);
        startAutoPlay();
    }

    /**
     * constructor
     *
     * @param context
     * @param attrSet
     */
    public BGABanner(Context context, AttrSet attrSet) {
        super(context, attrSet);
        this.context = context;
        initDefaultAttrs(context);
        initCustomAttr(attrSet);
        initView(context);
        setTouchEventListener(this);
        setEstimateSizeListener(this);
    }

    private BGAOnNoDoubleClickListener mGuideOnNoDoubleClickListener = new BGAOnNoDoubleClickListener() {
        @Override
        public void onNoDoubleClick(Component v) {
            if (mGuideDelegate != null) {
                mGuideDelegate.onClickEnterOrSkip();
            }
        }
    };

    public PageTransformer getPageTransformer() {
        return pageTransformer;
    }

    public void setPageTransformer(PageTransformer pageTransformer) {
        this.pageTransformer = pageTransformer;
    }

    private void initDefaultAttrs(Context context) {
        mAutoPlayTask = new AutoPlayTask(this);
        handler = new EventHandler(EventRunner.getMainEventRunner());

        // 指示器之间的间隔
        indicatorLeftRightMargin = DensityUtils.fpToPx(context, 3);
        indicatorTopBottomMargin = DensityUtils.fpToPx(context, 6);
        indicatorContainerLeftRightPadding = DensityUtils.fpToPx(context, 10);
        textSize = 10;
        numberTextSize = DensityUtils.fpToPx(context, 10);
        mContentBottomMargin = 0;
        heightRatio = 0;
    }

    @Override
    public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
        if (heightRatio > 0) {
            int width = MeasureSpec.getSize(widthMeasureSpec);
            int height = (int) (width / heightRatio);
            heightMeasureSpec = MeasureSpec.getMeasureSpec(height, MeasureSpec.PRECISE);
            setHeight(heightMeasureSpec);
            invalidate();
        }
        return false;
    }

    private void initCustomAttr(AttrSet attr) {
        // app:banner_dicatorLeftOrRightOrCenter="" 可选值 left right center
        dicatorLeftOrRightOrCenterStr = TypedAttrUtils.getString(attr, "banner_dicatorLeftOrRightOrCenter", dicatorLeftOrRightOrCenterStr);
        dicatorLeftOrRightOrCenter = setgravityStyle(dicatorLeftOrRightOrCenterStr);
        dicatorTextLeftOrRightOrCenter = TypedAttrUtils.getString(attr, "banner_dicatorTextLeftOrRightOrCenter", dicatorTextLeftOrRightOrCenter);
        dicatorTextGravity = setgravityStyle(dicatorTextLeftOrRightOrCenter);
        indicator_TopOrBottomStr = TypedAttrUtils.getString(attr, "banner_indicator_TopOrBottom", indicator_TopOrBottomStr);
        indicator_TopOrBottom = setgravityTopOrBottom(indicator_TopOrBottomStr);
        indicatorLeftRightMargin = TypedAttrUtils.getIntValue(attr, "banner_pointLeftRightMargin", indicatorLeftRightMargin);
        indicatorTopBottomMargin = TypedAttrUtils.getIntValue(attr, "banner_pointTopBottomMargin", indicatorTopBottomMargin);
        indicatorContainerLeftRightPadding = TypedAttrUtils.getIntValue(attr, "banner_pointContainerLeftRightPadding", indicatorContainerLeftRightPadding);

        // 是否自动轮播
        isAuto = TypedAttrUtils.getBoolean(attr, "banner_pointAutoPlayAble", isAuto);
        autoTime = TypedAttrUtils.getIntValue(attr, "banner_pointAutoPlayInterval", autoTime);
        switchPageTime = TypedAttrUtils.getIntValue(attr, "banner_pageChangeDuration", switchPageTime);
        textColor = TypedAttrUtils.getColor(attr, "banner_tipTextColor", textColor);
        textSize = TypedAttrUtils.getIntValue(attr, "banner_tipTextSize", textSize);

        // 占位图片
        isNumType = TypedAttrUtils.getBoolean(attr, "banner_isNumberIndicator", isNumType);
        numberTextColor = TypedAttrUtils.getColor(attr, "banner_numberIndicatorTextColor", numberTextColor);
        numberTextSize = TypedAttrUtils.getIntValue(attr, "banner_numberIndicatorTextSize", numberTextSize);
        numberindicatorbackground = TypedAttrUtils.getColor(attr, "banner_numberIndicatorBackground", numberindicatorbackground);
        indicatorBgColor = TypedAttrUtils.getColor(attr, "banner_pointContainerBackground", numberindicatorbackground);
        isOnlyOnePageShow = TypedAttrUtils.getBoolean(attr, "banner_isNeedShowIndicatorOnOnlyOnePage", isOnlyOnePageShow);
        mContentBottomMargin = TypedAttrUtils.getIntValue(attr, "banner_contentBottomMargin", mContentBottomMargin);
        heightRatio = TypedAttrUtils.getFloatSize(attr, "banner_aspectRatio", heightRatio);
        final int index = TypedAttrUtils.getIntValue(attr, "ohos:scale_mode", -1);
        if (index >= 0 && index < sScaleTypeArray.length) {
            mScaleType = sScaleTypeArray[index];
        }
    }

    private void initView(Context context) {
        // 最外层  banner 自身高度
        zsqPostionLayout = new DependentLayout(context);
        ShapeElement shapeElement = new ShapeElement();
        zsqPostionLayout.setBackground(shapeElement);
        DependentLayout.LayoutConfig postionConfig = new DependentLayout.LayoutConfig(RMP, RWC);

        // ========================== 控制指示器在上还是在下 =====================================
        if (indicator_TopOrBottom == LayoutConfig.ALIGN_PARENT_TOP) {
            postionConfig.addRule(LayoutConfig.ALIGN_PARENT_TOP);
        } else {
            postionConfig.addRule(LayoutConfig.ALIGN_PARENT_BOTTOM);
        }

        // 添加指示器区域
        addComponent(zsqPostionLayout, postionConfig);

        // 圆点指示器
        DependentLayout.LayoutConfig indicatorConfig = new DependentLayout.LayoutConfig(RWC, RWC);

        // 指示器在屏幕的区域（在zsqPostionLayout区域内的位置）
        indicatorConfig.addRule(dicatorLeftOrRightOrCenter);
        indicatorConfig.addRule(LayoutConfig.VERTICAL_CENTER);

        // 指示器
        DependentLayout indicatorLayout = new DependentLayout(getContext());

        // 指示器的背景颜色
        ShapeElement indicatorElement = new ShapeElement();
        if (indicatorBgColor == null) {
            indicatorElement.setRgbColor(RgbColor.fromArgbInt(Color.TRANSPARENT.getValue()));
        } else {
            indicatorElement.setRgbColor(RgbColor.fromArgbInt(indicatorBgColor.getValue()));
        }
        indicatorLayout.setBackground(indicatorElement);
        DependentLayout.LayoutConfig indicatorLayoutConfig = new DependentLayout.LayoutConfig(RMP, RWC);
        indicatorLayoutConfig.addRule(LayoutConfig.ALIGN_PARENT_BOTTOM);
        indicatorConfig.setMarginBottom(DensityUtils.fpToPx(getContext(), indicatorTopBottomMargin));

        // 居两边的位置
        indicatorConfig.setMarginsLeftAndRight(indicatorContainerLeftRightPadding, indicatorContainerLeftRightPadding);

        if (isNumType) {
            // ==========================数字指示器（示例：1/5）=====================================
            mNumberIndicatorTv = new Text(context);
            mNumberIndicatorTv.setId(numIndicatorId);
            mNumberIndicatorTv.setTextAlignment(TextAlignment.VERTICAL_CENTER);
            mNumberIndicatorTv.setMultipleLine(false);
            mNumberIndicatorTv.setTruncationMode(ELLIPSIS_AT_END);
            mNumberIndicatorTv.setTextColor(numberTextColor);
            mNumberIndicatorTv.setTextSize(numberTextSize, Text.TextSizeType.FP);
            mNumberIndicatorTv.setVisibility(Component.INVISIBLE);
            if (numberindicatorbackground != null) {
                ShapeElement backgraound = new ShapeElement();
                backgraound.setRgbColor(RgbColor.fromRgbaInt(numberindicatorbackground.getValue()));
                mNumberIndicatorTv.setBackground(backgraound);
            }
            indicatorLayout.addComponent(mNumberIndicatorTv, indicatorConfig);
        } else {
            // ==========================非数字指示器=====================================
            mPointRealContainerLl = new DirectionalLayout(context);
            mPointRealContainerLl.setId(mPointRealContainerLlId);
            mPointRealContainerLl.setOrientation(DirectionalLayout.HORIZONTAL);
            mPointRealContainerLl.setAlignment(CENTER);
            indicatorLayout.addComponent(mPointRealContainerLl, indicatorConfig);
        }

        // ==========================指示器 文字属性  文字默认居左=====================================
        DependentLayout.LayoutConfig textLayoutConfig = new DependentLayout.LayoutConfig(RWC, RWC);
        textLayoutConfig.setMarginsLeftAndRight(indicatorContainerLeftRightPadding, indicatorContainerLeftRightPadding);
        textLayoutConfig.addRule(dicatorTextGravity);
        textLayoutConfig.addRule(LayoutConfig.VERTICAL_CENTER);
        indicatorText = new Text(context);
        indicatorText.setTextAlignment(TextAlignment.VERTICAL_CENTER);
        indicatorText.setMultipleLine(false);
        indicatorText.setTruncationMode(ELLIPSIS_AT_END);
        indicatorText.setTextColor(textColor);
        indicatorText.setTextSize(textSize, Text.TextSizeType.FP);

        // 添加文字
        indicatorLayout.addComponent(indicatorText, textLayoutConfig);

        // 添加指示器
        zsqPostionLayout.addComponent(indicatorLayout, indicatorLayoutConfig);
        showPlaceholder();
    }

    /**
     * showPlaceholder
     */
    public void showPlaceholder() {
        if (mPlaceholderIv == null && placeholderDrawableResId != -1) {
            mPlaceholderIv = BGABannerUtil.getItemImageView(context, placeholderDrawableResId, new BGALocalImageSize(720, 360, 640, 320), mScaleType);
            DependentLayout.LayoutConfig layoutParams = new DependentLayout.LayoutConfig(RMP, RMP);
            layoutParams.setMargins(0, 0, 0, mContentBottomMargin);
            addComponent(mPlaceholderIv, layoutParams);
        }
    }

    /**
     * 设置页码切换过程的时间长度
     *
     * @param duration 页码切换过程的时间长度
     */
    public void setPageChangeDuration(int duration) {
        if (duration >= 0 && duration <= 2000) {
            switchPageTime = duration;
            if (mViewPager != null) {
                mViewPager.setPageSwitchTime(duration);
            }
        }
    }

    /**
     * 设置是否开启自动轮播，需要在 setData 方法之前调用，并且调了该方法后必须再调用一次 setData 方法
     * 例如根据图片当图片数量大于 1 时开启自动轮播，等于 1 时不开启自动轮播
     *
     * @param autoPlayAble 设置自动轮播
     */
    public void setAutoPlayAble(boolean autoPlayAble) {
        isAuto = autoPlayAble;
        stopAutoPlay();
        if (mViewPager != null && mViewPager.getProvider() != null) {
            mViewPager.getProvider().notifyDataChanged();
        }
    }

    /**
     * 设置自动轮播的时间间隔
     *
     * @param autoPlayInterval 设置自动轮播的时间间隔
     */
    public void setAutoPlayInterval(int autoPlayInterval) {
        autoTime = autoPlayInterval;
    }

    /**
     * 设置每一页的控件、数据模型和文案
     *
     * @param views 每一页的控件集合
     * @param models 每一页的数据模型集合
     * @param tips 每一页的提示文案集合
     */
    public void setData(List<Component> views, List<? extends Object> models, List<String> tips) {
        if (BGABannerUtil.isCollectionEmpty(views)) {
            isAuto = false;
            views = new ArrayList<>();
            models = new ArrayList<>();
            tips = new ArrayList<>();
        }
        if (isAuto && views.size() < 3 && mHackyViews == null) {
            isAuto = false;
        }

        mModels = models;
        mViews = views;
        mTips = tips;

        initIndicator();
        initViewPager();
        removePlaceholder();
    }

    /**
     * 设置布局资源id、数据模型和文案
     *
     * @param layoutResId item布局文件资源id
     * @param models 每一页的数据模型集合
     * @param tips 每一页的提示文案集合
     */
    public void setData(int layoutResId, List<? extends Object> models, List<String> tips) {
        mViews = new ArrayList<>();
        if (models == null) {
            models = new ArrayList<>();
            tips = new ArrayList<>();
        }
        for (int i = 0; i < models.size(); i++) {
            mViews.add(inflateItemView(layoutResId));
        }
        if (isAuto && mViews.size() < 3) {
            mHackyViews = new ArrayList<>(mViews);
            mHackyViews.add(inflateItemView(layoutResId));
            if (mHackyViews.size() == 2) {
                mHackyViews.add(inflateItemView(layoutResId));
            }
        }
        setData(mViews, models, tips);
    }

    private Component inflateItemView(int layoutResId) {
        Component itemView = LayoutScatter.getInstance(context).parse(layoutResId, null, false);
        if (itemView instanceof Image) {
            ((Image) itemView).setScaleMode(mScaleType);
        }
        return itemView;
    }

    /**
     * 设置数据模型和文案，布局资源默认为 ImageView
     *
     * @param models 每一页的数据模型集合
     * @param tips 每一页的提示文案集合
     */
    public void setData(List<? extends Object> models, List<String> tips) {
        setData(ResourceTable.Layout_bga_banner_item_image, models, tips);
    }

    /**
     * 设置每一页的控件集合，主要针对引导页的情况
     *
     * @param views 每一页的控件集合
     */
    public void setData(List<Component> views) {
        setData(views, null, null);
    }

    /**
     * 设置每一页图片的资源 id，主要针对引导页的情况
     *
     * @param imageSize 长度
     * @param scaleType 类型
     * @param resIds 每一页图片资源 id
     */
    public void setData(BGALocalImageSize imageSize, Image.ScaleMode scaleType, int... resIds) {
        if (imageSize == null) {
            imageSize = new BGALocalImageSize(0, 0, 0, 0);
        }
        if (scaleType != null) {
            mScaleType = scaleType;
        }
        List<Component> views = new ArrayList<>();
        for (int resId : resIds) {
            views.add(BGABannerUtil.getItemImageView(getContext(), resId, imageSize, mScaleType));
        }
        setData(views);
    }

    /**
     * 设置是否允许用户手指滑动
     *
     * @param allowUserScrollable true表示允许跟随用户触摸滑动，false反之
     */
    public void setAllowUserScrollable(boolean allowUserScrollable) {
        mAllowUserScrollable = allowUserScrollable;
        if (mViewPager != null) {
            mViewPager.setSlidingPossible(mAllowUserScrollable);
        }
    }

    /**
     * 添加ViewPager滚动监听器
     *
     * @param onPageChangeListener 监听
     */
    public void setOnPageChangeListener(PageSliders.PageChangedListener onPageChangeListener) {
        mOnPageChangeListener = onPageChangeListener;
    }

    /**
     * 设置进入按钮和跳过按钮控件资源 id，需要开发者自己处理这两个按钮的点击事件
     *
     * @param enterResId 进入按钮控件
     * @param skipResId 跳过按钮控件
     */
    public void setEnterSkipViewId(int enterResId, int skipResId) {
        if (enterResId != 0) {
            mEnterView = ((Ability) getContext()).findComponentById(enterResId);
        }
        if (skipResId != 0) {
            mSkipView = ((Ability) getContext()).findComponentById(skipResId);
        }
    }

    /**
     * 设置进入按钮和跳过按钮控件资源 id 及其点击事件监听器
     * 如果进入按钮和跳过按钮有一个不存在的话就传 0
     * 在 BGABanner 里已经帮开发者处理了重复点击事件
     * 在 BGABanner 里已经帮开发者处理了「跳过按钮」和「进入按钮」的显示与隐藏
     *
     * @param ability 页面
     * @param enterResId 进入按钮控件资源 id，没有的话就传 0
     * @param skipResId 跳过按钮控件资源 id，没有的话就传 0
     * @param guideDelegate 引导页「进入」和「跳过」按钮点击事件监听器
     */
    public void setEnterSkipViewIdAndDelegate(Ability ability, int enterResId, int skipResId, GuideDelegate guideDelegate) {
        if (guideDelegate != null) {
            mGuideDelegate = guideDelegate;
            if (enterResId != 0) {
                mEnterView = ability.findComponentById(enterResId);
                mEnterView.setClickedListener(mGuideOnNoDoubleClickListener);
            }
            if (skipResId != 0) {
                mSkipView = ability.findComponentById(skipResId);
                mSkipView.setClickedListener(mGuideOnNoDoubleClickListener);
            }
        }
    }

    /**
     * 获取当前选中界面索引
     *
     * @return 返回索引
     */
    public int getCurrentItem() {
        if (mViewPager == null || BGABannerUtil.isCollectionEmpty(mViews)) {
            return -1;
        } else {
            return mViewPager.getCurrentPage() % mViews.size();
        }
    }

    /**
     * 获取广告页面总个数
     *
     * @return 返回页面总个数
     */
    public int getItemCount() {
        return mViews == null ? 0 : mViews.size();
    }

    /**
     * getViews
     *
     * @return components
     */
    public List<? extends Component> getViews() {
        return mViews;
    }

    /**
     * getItemView
     *
     * @param position
     * @param <VT>
     * @return view at position
     */
    public <VT extends Component> VT getItemView(int position) {
        return mViews == null ? null : (VT) mViews.get(position);
    }

    /**
     * getItemImageView
     *
     * @param position
     * @return item view at position
     */
    public Image getItemImageView(int position) {
        return getItemView(position);
    }

    /**
     * getTips
     *
     * @return tips
     */
    public List<String> getTips() {
        return mTips;
    }

    /**
     * getViewPager
     *
     * @return PageSlider
     */
    public PageSliders getViewPager() {
        return mViewPager;
    }

    /**
     * setIndicatorVisibility
     *
     * @param visible
     */
    public void setIndicatorVisibility(boolean visible) {
        zsqPostionLayout.setVisibility(visible ? VISIBLE : Component.INVISIBLE);
    }

    /**
     * setIndicatorTopBottomMarginDp
     *
     * @param marginDp
     */
    public void setIndicatorTopBottomMarginDp(int marginDp) {
        setIndicatorTopBottomMarginPx(DensityUtils.fpToPx(getContext(), marginDp));
    }

    /**
     * setIndicatorTopBottomMarginRes
     *
     * @param resId
     * @throws IOException
     * @throws NotExistException
     * @throws WrongTypeException
     */
    public void setIndicatorTopBottomMarginRes(int resId) throws IOException, NotExistException, WrongTypeException {
        setIndicatorTopBottomMarginPx(getResourceManager().getElement(resId).getInteger());
    }

    /**
     * setIndicatorTopBottomMarginPx
     *
     * @param marginPx
     */
    public void setIndicatorTopBottomMarginPx(int marginPx) {
        indicatorTopBottomMargin = marginPx;
        zsqPostionLayout.setMarginsTopAndBottom(indicatorTopBottomMargin, indicatorTopBottomMargin);
    }

    private void initIndicator() {
        if (mPointRealContainerLl != null) {
            mPointRealContainerLl.removeAllComponents();
            if (isOnlyOnePageShow || (!isOnlyOnePageShow && mViews.size() > 1)) {
                DirectionalLayout.LayoutConfig lp = new DirectionalLayout.LayoutConfig(DensityUtils.fpToPx(getContext(), 15), DensityUtils.fpToPx(getContext(), 15));
                lp.setMargins(indicatorLeftRightMargin, 0, indicatorLeftRightMargin, 0);
                Image imageView;
                for (int i = 0; i < mViews.size(); i++) {
                    imageView = new Image(getContext());
                    imageView.setLayoutConfig(lp);

                    // 白色菱形
                    imageView.setScale(1.5f, 1.5f);
                    mPointRealContainerLl.addComponent(imageView);
                }
            }
        }
        if (mNumberIndicatorTv != null) {
            if (isOnlyOnePageShow || (!isOnlyOnePageShow && mViews.size() > 1)) {
                mNumberIndicatorTv.setVisibility(Component.VISIBLE);
            } else {
                mNumberIndicatorTv.setVisibility(Component.INVISIBLE);
            }
        }
    }

    /**
     * setIconYesElement
     *
     * @param iconYesElement
     */
    public void setIconYesElement(int iconYesElement) {
        this.iconYesElement = iconYesElement;
        postLayout();
    }

    /**
     * setIconNoElement
     *
     * @param iconNoElement
     */
    public void setIconNoElement(int iconNoElement) {
        this.iconNoElement = iconNoElement;
        postLayout();
    }

    /**
     * switchToPoint
     *
     * @param newCurrentPoint
     */
    public void switchToPoint(int newCurrentPoint) {
        if (indicatorText != null) {
            if (mTips == null || mTips.size() < 1 || newCurrentPoint >= mTips.size()) {
                indicatorText.setVisibility(Component.HIDE);
            } else {
                indicatorText.setVisibility(Component.VISIBLE);
                indicatorText.setText(mTips.get(newCurrentPoint));
            }
        }

        if (mPointRealContainerLl != null) {
            if (mViews != null && mViews.size() > 0 && newCurrentPoint < mViews.size() && (isOnlyOnePageShow || mViews.size() > 1)) {
                for (int i = 0; i < mPointRealContainerLl.getChildCount(); i++) {
                    mPointRealContainerLl.getComponentAt(i).setSelected(i == newCurrentPoint);
                    if (i == newCurrentPoint && iconYesElement != 9) {
                        if (iconYesElement != -1) {
                            ((Image) mPointRealContainerLl.getComponentAt(i)).setPixelMap(iconYesElement);
                        } else {
                            ((Image) mPointRealContainerLl.getComponentAt(i)).setPixelMap(ResourceTable.Media_yuan_yes);
                        }
                    } else {
                        if (iconNoElement != -1) {
                            ((Image) mPointRealContainerLl.getComponentAt(i)).setPixelMap(iconNoElement);
                        } else {
                            ((Image) mPointRealContainerLl.getComponentAt(i)).setPixelMap(ResourceTable.Media_yuan_no);
                        }
                    }

                    // 处理指示器选中和未选中状态图片尺寸不相等
                    mPointRealContainerLl.getComponentAt(i).postLayout();
                }
            } else {
                mPointRealContainerLl.setVisibility(Component.HIDE);
            }
            postLayout();
        }

        if (mNumberIndicatorTv != null) {
            if (mViews != null && mViews.size() > 0 && newCurrentPoint < mViews.size() && (isOnlyOnePageShow || mViews.size() > 1)) {
                mNumberIndicatorTv.setVisibility(Component.VISIBLE);
                mNumberIndicatorTv.setText((newCurrentPoint + 1) + "/" + mViews.size());
            } else {
                mNumberIndicatorTv.setVisibility(Component.HIDE);
            }
        }
    }

    private void initViewPager() {
        if (mViewPager != null && this.equals(mViewPager.getComponentParent())) {
            this.removeComponent(mViewPager);
            mViewPager = null;
        }
        mViewPager = new BGAViewPager(getContext());
        LayoutConfig layoutConfig = new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_PARENT);
        mViewPager.setLayoutConfig(layoutConfig);
        mViewPager.setPageCacheSize(1);
        mViewPager.setProvider(new PageAdapter());
        mViewPager.setViewCache(mViewCache);
        mViewPager.setPageChangedListener(this);

        // 设置是否允许用户手指滑动
        mViewPager.setSlidingPossible(mAllowUserScrollable);
        mViewPager.setPageTransformer(pageTransformer);
        setPageChangeDuration(switchPageTime);
        DependentLayout.LayoutConfig layoutParams = new DependentLayout.LayoutConfig(RMP, RMP);

        // 占位图高度
        layoutParams.setMargins(0, 0, 0, mContentBottomMargin);
        addComponent(mViewPager, 0, layoutParams);
        if (isAuto && !BGABannerUtil.isCollectionEmpty(mViews)) {
            int zeroItem = Integer.MAX_VALUE / 2 - (Integer.MAX_VALUE / 2) % mViews.size();
            mViewPager.setCurrentPage(zeroItem);
            startAutoPlay();
        } else {
            switchToPoint(0);
        }
    }

    /**
     * removePlaceholder
     */
    public void removePlaceholder() {
        if (mPlaceholderIv != null) {
            removeComponent(mPlaceholderIv);
            mPlaceholderIv = null;
        }
    }

    /**
     * setAspectRatio
     *
     * @param aspectRatio 设置宽高比例
     */
    public void setAspectRatio(float aspectRatio) {
        heightRatio = aspectRatio;
        postLayout();
    }

    /**
     * 设置当只有一页数据时是否显示指示器
     *
     * @param isNeedShowIndicatorOnOnlyOnePage 设置当只有一页数据时是否显示指示器
     */

    public void setIsNeedShowIndicatorOnOnlyOnePage(boolean isNeedShowIndicatorOnOnlyOnePage) {
        isOnlyOnePageShow = isNeedShowIndicatorOnOnlyOnePage;
    }

    /**
     * setCurrentItem
     *
     * @param item
     */
    public void setCurrentItem(int item) {
        if (mViewPager == null || mViews == null || item > getItemCount() - 1) {
            return;
        }

        if (isAuto) {
            int realCurrentItem = mViewPager.getCurrentPage();
            int currentItem = realCurrentItem % mViews.size();
            int offset = item - currentItem;

            // 这里要使用循环递增或递减设置，否则会ANR
            if (offset < 0) {
                for (int i = -1; i >= offset; i--) {
                    mViewPager.setCurrentPage(realCurrentItem + i, false);
                }
            } else if (offset > 0) {
                for (int i = 1; i <= offset; i++) {
                    mViewPager.setCurrentPage(realCurrentItem + i, false);
                }
            }

            startAutoPlay();
        } else {
            mViewPager.setCurrentPage(item, false);
        }
    }

    private void onInvisibleToUser() {
        // 处理 RecyclerView 中从对用户不可见变为可见时卡顿的问题
        if (!mIsFirstInvisible && isAuto && mViewPager != null && getItemCount() > 0 && mPageScrollPositionOffset != 0) {
            mViewPager.setCurrentPage(mViewPager.getCurrentPage() - 1);
            mViewPager.setCurrentPage(mViewPager.getCurrentPage() + 1);
        }
        mIsFirstInvisible = false;
    }

    @Override
    public void release() {
        super.release();
        onInvisibleToUser();
    }

    /**
     * startAutoPlay
     */
    public void startAutoPlay() {
        stopAutoPlay();
        if (isAuto) {
            handler.postTask(mAutoPlayTask, autoTime);
        }
    }

    /**
     * stopAutoPlay
     */
    public void stopAutoPlay() {
        if (mAutoPlayTask != null) {
            handler.removeTask(mAutoPlayTask);
        }
    }

    /**
     * 设置页面切换换动画
     *
     * @param effect 设置页面切换换动画
     */
    public void setTransitionEffect(TransitionEffect effect) {
        if (mViewPager != null) {
            initViewPager();
            if (mHackyViews == null) {
                BGABannerUtil.resetPageTransformer(mViews);
            } else {
                BGABannerUtil.resetPageTransformer(mHackyViews);
            }
        }
    }

    /**
     * 切换到下一页
     */
    private void switchToNextPage() {
        if (mViewPager != null) {
            mViewPager.setCurrentPage(mViewPager.getCurrentPage() + 1);
        }
    }

    private void handleGuideViewVisibility(int position, float positionOffset) {
        if (mEnterView == null && mSkipView == null) {
            return;
        }
        if (getItemCount() < 2) {
            if (mEnterView != null) {
                mEnterView.setVisibility(VISIBLE);
                if (mSkipView != null) {
                    mSkipView.setVisibility(HIDE);
                }
                return;
            }
            if (mSkipView != null) {
                mSkipView.setVisibility(VISIBLE);
                return;
            }
        }

        if (position == getItemCount() - 2) {
            if (mEnterView != null) {
                mEnterView.setAlpha(positionOffset);
            }
            if (mSkipView != null) {
                mSkipView.setAlpha(1.0f - positionOffset);
            }

            if (positionOffset > 0.5f) {
                if (mEnterView != null) {
                    mEnterView.setVisibility(VISIBLE);
                }
                if (mSkipView != null) {
                    mSkipView.setVisibility(HIDE);
                }
            } else {
                if (mEnterView != null) {
                    mEnterView.setVisibility(HIDE);
                }
                if (mSkipView != null) {
                    mSkipView.setVisibility(VISIBLE);
                }
            }
        } else if (position == getItemCount() - 1) {
            if (mEnterView != null) {
                mEnterView.setAlpha(1.0f - positionOffset);
            }
            if (mSkipView != null) {
                mSkipView.setAlpha(positionOffset);
            }
        } else {
            if (mSkipView != null) {
                mSkipView.setVisibility(VISIBLE);
                mSkipView.setAlpha(1.0f);
            }
            if (mEnterView != null) {
                mEnterView.setVisibility(HIDE);
            }
        }
    }

    /**
     * setDelegate
     *
     * @param delegate
     */
    public void setDelegate(Delegate<Component, Object> delegate) {
        mDelegate = delegate;
    }

    /**
     * setAdapter
     *
     * @param adapter
     */
    public void setAdapter(Adapter<Component, Object> adapter) {
        mAdapter = adapter;
    }

    @Override
    public void onPageSliding(int position, float positionOffset, int positionOffsetPixels) {
        if (BGABannerUtil.isCollectionEmpty(mViews)) {
            return;
        }
        mPageScrollPositionOffset = positionOffset;

        if (indicatorText != null) {
            if (BGABannerUtil.isCollectionNotEmpty(mTips)) {
                indicatorText.setVisibility(VISIBLE);
            } else {
                indicatorText.setVisibility(HIDE);
            }
        }

        if (mOnPageChangeListener != null) {
            mOnPageChangeListener.onPageSliding(position % mViews.size(), positionOffset, positionOffsetPixels);
        }
    }

    private float getClientWidth() {
        return getEstimatedWidth() - getPaddingLeft() - getPaddingRight();
    }

    @Override
    public void onPageSlideStateChanged(int state) {
        // 滑动过程中回调
        if (mOnPageChangeListener != null) {
            mOnPageChangeListener.onPageSlideStateChanged(state);
        }
    }

    @Override
    public void onPageChosen(int position) {
        // 只有一页的时候
        changeGuide(position);
        if (BGABannerUtil.isCollectionEmpty(mViews)) {
            return;
        }
        position = position % mViews.size();
        switchToPoint(position);

        if (mOnPageChangeListener != null) {
            mOnPageChangeListener.onPageChosen(position);
        }
    }

    private void changeGuide(int position) {
        if (mEnterView == null && mSkipView == null) {
            return;
        }
        if (mViews.size() <= 1) {
            if (mEnterView != null) {
                mEnterView.setVisibility(VISIBLE);
                if (mSkipView != null) {
                    mSkipView.setVisibility(HIDE);
                }
            }
            if (mSkipView != null) {
                mSkipView.setVisibility(VISIBLE);
            }
        } else {
            mSkipView.setVisibility(HIDE);
            mEnterView.setVisibility(HIDE);
        }

        // 最后一页显示
        if (position % mViews.size() == getItemCount() - 1) {
            // 只有一页时，有进入按钮则显示进入按钮并隐藏跳过按钮，没有进入按钮则显示跳过按钮
            if (mEnterView != null) {
                mEnterView.setVisibility(VISIBLE);
                if (mSkipView != null) {
                    mSkipView.setVisibility(HIDE);
                    mPointRealContainerLl.setVisibility(INVISIBLE);
                }
            }
        } else {
            if (mSkipView != null) {
                mSkipView.setVisibility(VISIBLE);
                mSkipView.setAlpha(1.0f);
            }
            if (mEnterView != null) {
                mEnterView.setVisibility(HIDE);
            }
        }
    }

    /**
     * getmViewPager
     *
     * @return BGAViewPager
     */
    public BGAViewPager getmViewPager() {
        return mViewPager;
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        if (isAuto) {
            switch (touchEvent.getAction()) {
                case TouchEvent.PRIMARY_POINT_DOWN:
                    stopAutoPlay();
                    break;
                case TouchEvent.PRIMARY_POINT_UP:
                case TouchEvent.CANCEL:
                    startAutoPlay();
                    break;
                default:
                    break;
            }
        }
        return false;
    }

    /**
     * PageAdapter
     */
    private class PageAdapter extends PageSliderProvider {
        @Override
        public int getCount() {
            return mViews == null ? 0 : (isAuto ? Integer.MAX_VALUE : mViews.size());
        }

        @Override
        public Object createPageInContainer(ComponentContainer container, int position) {
            if (BGABannerUtil.isCollectionEmpty(mViews)) {
                return null;
            }

            final int finalPosition = position % mViews.size();
            Component view;
            if (mHackyViews == null) {
                view = mViews.get(finalPosition);
            } else {
                view = mHackyViews.get(position % mHackyViews.size());
            }

            mViewCache.put(position, view);

            if (mDelegate != null) {
                view.setClickedListener(new BGAOnNoDoubleClickListener() {
                    @Override
                    public void onNoDoubleClick(Component component) {
                        int currentPosition = mViewPager.getCurrentPage() % mViews.size();
                        if (BGABannerUtil.isIndexNotOutOfBounds(currentPosition, mModels)) {
                            mDelegate.onBannerItemClick(BGABanner.this, component, mModels.get(currentPosition), currentPosition);
                        } else if (BGABannerUtil.isCollectionEmpty(mModels)) {
                            mDelegate.onBannerItemClick(BGABanner.this, component, null, currentPosition);
                        }
                    }
                });
            }

            if (mAdapter != null) {
                if (BGABannerUtil.isIndexNotOutOfBounds(finalPosition, mModels)) {
                    mAdapter.fillBannerItem(BGABanner.this, view, mModels.get(finalPosition), finalPosition);
                } else if (BGABannerUtil.isCollectionEmpty(mModels)) {
                    mAdapter.fillBannerItem(BGABanner.this, view, null, finalPosition);
                }
            }

            ComponentParent viewParent = view.getComponentParent();
            if (viewParent != null) {
                viewParent.removeComponent(view);
            }
            container.addComponent(view);
            return view;
        }

        @Override
        public void destroyPageFromContainer(ComponentContainer componentContainer, int i, Object o) {
        }

        @Override
        public boolean isPageMatchToObject(Component view, Object object) {
            return view.equals(object);
        }
    }

    /**
     * AutoPlayTask
     */
    private static class AutoPlayTask implements Runnable {
        private final WeakReference<BGABanner> mBanner;

        private AutoPlayTask(BGABanner banner) {
            mBanner = new WeakReference<>(banner);
        }

        @Override
        public void run() {
            BGABanner banner = mBanner.get();
            if (banner != null) {
                banner.startAutoPlay();
                banner.switchToNextPage();
            }
        }
    }

    /**
     * item 点击事件监听器，在 BGABanner 里已经帮开发者处理了防止重复点击事件
     *
     * @param <V> item 视图类型，如果没有在 setData 方法里指定自定义的 item 布局资源文件的话，这里的 V 就是 ImageView
     * @param <M> item 数据模型
     */
    public interface Delegate<V extends Component, M> {
        /**
         * onBannerItemClick
         *
         * @param banner
         * @param itemView
         * @param model
         * @param position
         */
        void onBannerItemClick(BGABanner banner, V itemView, M model, int position);
    }

    /**
     * 适配器，在 fillBannerItem 方法中填充数据，加载网络图片等
     *
     * @param <V> item 视图类型，如果没有在 setData 方法里指定自定义的 item 布局资源文件的话，这里的 V 就是 ImageView
     * @param <M> item 数据模型
     */

    public interface Adapter<V extends Component, M> {
        /**
         * fillBannerItem
         *
         * @param banner
         * @param itemView
         * @param model
         * @param position
         */
        void fillBannerItem(BGABanner banner, V itemView, M model, int position);
    }

    /**
     * 引导页「进入」和「跳过」按钮点击事件监听器，在 BGABanner 里已经帮开发者处理了防止重复点击事件
     */
    public interface GuideDelegate {
        /**
         * onClickEnterOrSkip
         */
        void onClickEnterOrSkip();
    }

    /**
     * getmPlaceholderDrawableResId
     *
     * @return placeholderDrawableResId
     */
    public int getmPlaceholderDrawableResId() {
        return placeholderDrawableResId;
    }

    /**
     * setmPlaceholderDrawableResId
     *
     * @param mPlaceholderDrawableResId
     */
    public void setmPlaceholderDrawableResId(int mPlaceholderDrawableResId) {
        this.placeholderDrawableResId = mPlaceholderDrawableResId;
        postLayout();
    }

    /**
     * 指示器位置输入转换
     *
     * @param type 类型
     * @return LayoutConfig.ALIGN_PARENT_LEFT父组件左对齐
     */

    private int setgravityStyle(String type) {
        // 判断是否为空

        if (!type.isEmpty()) {
            if (IN_ALIGN_PARENT_LEFT.equals(type.toUpperCase(Locale.ROOT))) {
                return ALIGN_PARENT_LEFT;
            } else if (IN_ALIGN_PARENT_RIGHT.equals(type.toUpperCase(Locale.ROOT))) {
                return LayoutConfig.ALIGN_PARENT_RIGHT;
            } else if (TEXT_CENTER.equals(type.toUpperCase(Locale.ROOT))) {
                return LayoutConfig.HORIZONTAL_CENTER;
            }
        }
        return LayoutConfig.ALIGN_PARENT_LEFT;
    }

    private int setgravityTopOrBottom(String type) {
        // 判断是否为空
        if (!type.isEmpty()) {
            if (OUT_ALIGN_PARENT_TOP.equals(type.toUpperCase(Locale.ROOT))) {
                return LayoutConfig.ALIGN_PARENT_TOP;
            } else if (OUT_ALIGN_PARENT_BOTTOM.equals(type.toUpperCase(Locale.ROOT))) {
                return LayoutConfig.ALIGN_PARENT_BOTTOM;
            }
        }
        return LayoutConfig.ALIGN_PARENT_BOTTOM;
    }
}